1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.util.data_structures;
12 
13 public import hip.util.string: String;
14 struct Pair(A, B, string aliasA = "", string aliasB = "")
15 {
16 
17     A first;
18     B second;
19 
20     alias a = first;
21     alias b = second;
22 
23     static if(aliasA != "")
24         mixin("alias "~aliasA~" = first;");
25     static if(aliasB != "")
26         mixin("alias "~aliasB~" = second;");
27 }
28 struct Dirty(T)
29 {
30     T value;
31     private bool isDirty;
32 
33     auto opAssign(T value)
34     {
35         if(value != this.value) this.value = value, isDirty = true;
36         return this;
37     }
38     void clearDirtyFlag(){isDirty = false;}
39 }
40 
41 mixin template DirtyFlagFields(string flagName, T, string[] fields)
42 {
43     static foreach(f; fields)
44     {
45         mixin("T _",f,";",
46         "T ",f,"() => _",f,";",
47         "T ",f,"(T v){if(_",f," != v) ",flagName," = true; return _",f," = v;}");
48     }
49 }
50 
51 struct Tuple(Fields...)
52 {
53     Fields fields;
54     alias fields this;
55     pragma(inline, true) size_t length(){ return Fields.length; }
56 }
57 auto tuple(Args...)(Args a){ return Tuple!(Args)(a); }
58 
59 pragma(LDC_no_typeinfo)
60 struct DirtyFlagsCmp(alias flag, Fields...)
61 {
62     import std.typecons;
63     static if(is(__traits(parent, Fields[0]) == struct))
64     	private static alias P = __traits(parent, Fields[0])*;
65    	else
66     	private static alias P = __traits(parent, Fields[0]);
67 	P parent;
68     Tuple!(typeof(Fields)) base;
69 
70     this(P parent)
71     {
72         start(parent);
73     }
74 
75     void start(P parent)
76     {
77         this.parent = parent;
78         static foreach (i, f; Fields)
79             base[i] = __traits(child, parent, f);
80     }
81 
82     void update()
83     {
84         bool changed;
85         static foreach (i, f; Fields)
86         {
87             {
88                 alias T = typeof(f);
89                 changed = changed || (__traits(child, parent, f) !is base[i]);
90             }
91         }
92         __traits(child, parent, flag) = __traits(child, parent, flag) || changed;
93     }
94     alias opCall = update;
95 }
96 
97 
98 
99 /** 
100  * RangeMap allows specifying a range in which a value spams, quite useful for defining outcomes
101  *  based on an input, experience gain progression, etc. Example Usage:
102  *  ```d
103  *  RangeMap!(int, string) colorRanges = "White"; //Default is "White"
104  *  colorRanges[0..9] = "Red";
105  *  colorRanges[10..19] = "Green";
106  *  colorRanges[20..29] = "Blue"
107  *  
108  *  writeln(colorRanges[5]); //Prints "Red"
109  *  ```
110  */
111 struct RangeMap(K, V)
112 {
113     import hip.util.reflection:isNumeric;
114     @nogc:
115     static assert(isNumeric!K, "RangeMap key must be a numeric type");
116     protected Array!K ranges;
117     protected Array!V values;
118     protected V _default;
119 
120     /**
121     *   When the value is out of range, the value returned is the _default one.
122     */
123     ref RangeMap setDefault(V _default)
124     {
125         this._default = _default;
126         return this;
127     }
128     /** 
129      * Alternative to the slice syntax
130      */
131     ref RangeMap setRange(K a, K b, V value)
132     {
133         if(ranges == null)
134         {
135             ranges = Array!K(8);
136             values = Array!V(8);
137         }
138         int rLength = cast(int)ranges.length;
139         ranges.reserve(ranges.length+2);
140         if(a > b)
141         {
142             K temp = a;
143             a = b;
144             b = temp;
145         }
146         if(rLength != 0 && ranges[rLength-1] > a) //Silently ignore for now
147             return this;
148         ranges~=a;
149         ranges~=b;
150         values~=value;
151         return this;
152     }
153 
154     /**
155     *   Uses binary search for finding the value range.
156     */
157     V get(K value)
158     {
159         int l = 0;
160         int length = cast(int)ranges.length;
161         int r = length-1;
162 
163         while(l < r)
164         {
165             int m = cast(int)((l+r)/2);
166             if(m % 2 != 0)
167                 m--;
168             K k = ranges[m];
169             //Check if value is between a[m] and a[m-1]
170             if(m+1 < length && value >= k && value <= ranges[m+1])
171                 return values[cast(int)(m/2)];
172             else if(k < value)
173                 l = m + 1;
174             else if(m > value)
175                 r = m - 1;
176             //Check if both values on the right is greater than value
177             else if(m+1 < length && k > value && ranges[m+1] > value)
178                 break;
179         }
180         return _default;
181     }
182 
183     pragma(inline) auto ref opAssign(V value)
184     {
185         setDefault(value);
186         return this;
187     }
188     pragma(inline) V opSliceAssign(V value, K start, K end)
189     {
190         setRange(start, end, value);
191         return value;
192     }
193     pragma(inline) V opIndex(K index){return get(index);}
194 }
195 /**
196 *   RefCounted, Array @nogc, OutputRange compatible, it aims to bring the same result as one would have by using
197 *   int[], Array!int should be equivalent, any different behaviour should be contacted. 
198 *   It may use more memory than requested for not making reallocation a big bottleneck
199 */
200 pragma(LDC_no_typeinfo)
201 struct Array(T) 
202 {
203     size_t length;
204     T* data;
205     private size_t capacity;
206     private int* countPtr;
207     import core.stdc.stdlib:malloc, calloc;
208     import core.stdc.string:memcpy, memset;
209     import core.stdc.stdlib:realloc;
210 
211     this(this) @nogc
212     {
213         if(countPtr !is null)
214             *countPtr = *countPtr + 1;
215     }
216 
217     pragma(inline) int opApply(scope int delegate(size_t idx, ref T) dg)
218     {
219         int result = 0;
220         for(int i = 0; i < length; i++)
221         {
222             result = dg(i, data[i]);
223             if(result)
224                 break;
225         }
226         return result;
227     }
228 
229     pragma(inline) int opApply(scope int delegate(ref T) dg)
230     {
231         int result = 0;
232         for(int i = 0; i < length; i++)
233         {
234             result = dg(data[i]);
235             if(result) break;
236         }
237         return result;
238     }
239     private void initialize(size_t capacity) @nogc
240     {
241         this.data = cast(T*)calloc(capacity, T.sizeof);
242         this.capacity = capacity;
243         this.countPtr = cast(int*)malloc(int.sizeof);
244         *this.countPtr = 1;
245         this[0..capacity] = T.init;
246     }
247 
248     static Array!T opCall(size_t capacity = 1) @nogc
249     {
250         Array!T ret;
251         ret.initialize(capacity);
252         return ret;
253     }
254 
255     static Array!T opCall(size_t length, T value) @nogc
256     {
257         Array!T ret = Array!(T)(length);
258         ret.length = length;
259         ret[] = value;
260         return ret;
261     }
262     
263     static Array!T opCall(scope T[] arr) @nogc
264     {
265         Array!T ret = Array!(T)(arr.length);
266         ret.length = arr.length;
267         ret.data[0..ret.length] = arr[];
268         return ret;
269     }
270 
271     void* getRef()
272     {
273         *countPtr = *countPtr + 1;
274         return cast(void*)&this;
275     }
276 
277     pragma(inline) bool opEquals(R)(const R other) const
278     if(is(R == typeof(null)))
279     {
280         return data == null;
281     }
282     void dispose() @nogc
283     {
284         import core.stdc.stdlib:free;
285         free(data);
286         free(countPtr);
287     }
288     immutable(T*) ptr(){return cast(immutable(T*))data;}
289     size_t opDollar() @nogc {return length;}
290 
291     T[] opSlice() @nogc
292     {
293         return data[0..length];
294     }
295 
296     T[] opSlice(size_t start, size_t end) @nogc
297     {
298         return data[start..end];
299     }
300     auto ref opSliceAssign(T)(T value) @nogc
301     {
302         data[0..length] = value;
303         return this;
304     }
305     auto ref opSliceAssign(T)(T value, size_t start, size_t end) @nogc
306     {
307         data[start..end] = value;
308         return this;
309     }
310     import hip.util.reflection:isArray;
311     auto ref opAssign(Q)(Q value) @nogc
312     if(isArray!Q)
313     {
314         if(data == null)
315             data = cast(T*)malloc(T.sizeof*value.length);
316         else
317             data = cast(T*)realloc(data, T.sizeof*value.length);
318         length = value.length;
319         capacity = value.length;
320         memcpy(data, value.ptr, T.sizeof*value.length);
321         return this;
322     }
323 
324     ref auto opIndex(size_t index) @nogc
325     {
326         assert(index>= 0 && index < length, "Array out of bounds");
327         return data[index];
328     }
329 
330     pragma(inline) private void resize(uint newSize) @nogc
331     {
332         if(data == null || capacity == 0)
333             initialize(newSize);
334         else
335         {
336             data = cast(T*)realloc(data, newSize*T.sizeof);
337             capacity = newSize;
338         }
339     }
340     ///Guarantee that no allocation will occur for the specified reserve amount of memory
341     void reserve(size_t newSize){if(newSize > capacity)resize(cast(uint)newSize);}
342     auto ref opOpAssign(string op, Q)(Q value) @nogc if(op == "~")
343     {
344         if(*countPtr != 1)
345         {
346             //Save old data
347             T* oldData = data;
348             //Remove from the ref counter
349             *countPtr = *countPtr - 1;
350             //Re initialize
351             initialize(length);
352             memcpy(data, oldData, T.sizeof*length);
353         }
354         static if(is(Q == T))
355         {
356             if(data == null)
357                 initialize(1);
358             if(length + 1 >= capacity)
359                 resize(cast(uint)((length+1)*1.5));
360             data[length++] = value;
361         }
362         else static if(is(Q == T[]) || is(Q == Array!T))
363         {
364             if(data == null)
365                 initialize(value.length);
366             if(length + value.length >= capacity)
367                 resize(cast(uint)((length+value.length)*1.5));
368             memcpy(data+length, value.ptr, T.sizeof*value.length);
369             length+= value.length;    
370         }
371         return this;
372     }
373 
374     // String asString() @nogc
375     // {
376     //     return String(this[0..$]);
377     // }
378     void put(T data) @nogc {this~= data;}
379     ~this() @nogc
380     {
381         if(countPtr != null)
382         {
383             *countPtr = *countPtr - 1;
384             assert(*countPtr >= 0);
385             if(*countPtr == 0)
386                 dispose();
387             data = null;
388             countPtr = null;
389             length = 0;
390         }
391     }
392 }
393 
394 
395 private mixin template Array2DMixin(T)
396 {
397     int opApply(scope int delegate(ref T) dg)
398     {
399         int result = 0;
400         for(int i = 0; i < width*height; i++)
401         {
402             result = dg(data[i]);
403             if (result)
404                 break;
405         }
406         return result;
407     }
408 
409     private uint height;
410     private uint width;
411     @nogc int getWidth() const {return width;}
412     @nogc int getHeight() const {return height;}
413     @nogc T[] opSlice(size_t start, size_t end)
414     {
415         if(end < start)
416         {
417             size_t temp = end;
418             end = start; end = temp;
419         }
420         if(end > width*height)
421             end = width*height;
422         return data[start..end];
423     }
424     pragma(inline, true)
425     {
426         @nogc ref auto opIndex(size_t i,  size_t j){return data[i*width+j];}
427         @nogc ref auto opIndex(size_t i)
428         {
429             size_t temp = i*width;
430             return data[temp..temp+width];
431         }
432         @nogc auto opIndexAssign(T)(T value, size_t i, size_t j){return data[i*width+j] = value;}
433         @nogc size_t opDollar() const {return width*height;}
434         @nogc bool opCast() const{return data !is null;}
435     }
436 }
437 
438 /**
439 *   By using Array2D instead of T[][], only one array instance is created, not "n" arrays. So it is a lot
440 *   faster when you use that instead of array of arrays.
441 *   
442 *   Its main usage is for avoiding a[i][j] static array, and not needing to deal with array of arrays.
443 */
444 pragma(LDC_no_typeinfo)
445 struct Array2D(T)
446 {
447 
448     mixin Array2DMixin!T;
449     Array2D_GC!T toGC()
450     {
451         Array2D_GC!T ret = new Array2D_GC!T(width, height);
452         ret.data[0..width*height] = data[0..width*height];
453         destroy(this);
454 
455         return ret;
456     }
457     @nogc:
458     private T* data;
459     import core.stdc.stdlib;
460 
461     private int* countPtr;
462 
463     this(this){*countPtr = *countPtr + 1;}
464     this(uint lengthHeight, uint lengthWidth)
465     {
466         data = cast(T*)malloc((lengthHeight*lengthWidth)*T.sizeof);
467         countPtr = cast(int*)malloc(int.sizeof);
468         *countPtr = 0;
469         height = lengthHeight;
470         width = lengthWidth;
471     }
472     ~this()
473     {
474         if(countPtr == null)
475             return;
476         *countPtr = *countPtr - 1;
477         if(*countPtr <= 0)
478         {
479             free(data);
480             free(countPtr);
481             data = null;
482             countPtr = null;
483             width = height = 0;
484         }
485     }
486     
487 
488 }
489 
490 class Array2D_GC(T)
491 {
492     private T[] data;
493     this(uint lengthHeight, uint lengthWidth)
494     {
495         data = new T[](lengthHeight*lengthWidth);
496         width = lengthWidth;
497         height = lengthHeight;
498     }
499     mixin Array2DMixin!T;
500 }
501 
502 private uint hash_fnv1(T)(T value)
503 {
504     import hip.util.reflection:isArray, isPointer;
505     enum fnv_offset_basis = 0x811c9dc5;
506     enum fnv_prime = 0x01000193;
507 
508     byte[] data;
509     static if(isArray!T)
510         data = (cast(byte*)value.ptr)[0..value.length*T.sizeof];
511     else static if(is(T == interface) || is(T == class) || isPointer!T)
512         data = cast(byte[])(cast(void*)value)[0..(void*).sizeof];
513     else //Value types: int, float, struct, etc
514         data = (cast(byte*)&value)[0..T.sizeof];
515 
516     typeof(return) hash = fnv_offset_basis;
517 
518     foreach(byteFromData; data)
519     {
520         hash*= fnv_prime;
521         hash^= byteFromData;
522     }
523     return hash;
524 }
525 
526 struct Map(K, V)
527 {
528     import core.stdc.stdlib;
529     static enum initializationLength = 128;
530     private static enum increasingFactor = 1.5;
531     private static enum increasingThreshold = 0.7;
532     private alias hashFunc = hash_fnv1;
533 
534     private struct MapData
535     {
536         K key;
537         V value;
538     }
539     private struct MapBucket
540     {
541         MapData data;
542         MapBucket* next;
543     }
544     private Array!MapBucket dataSet;
545 
546     private int* countPtr;
547 
548     this(this)
549     {
550         if(countPtr !is null)
551             *countPtr = *countPtr + 1;
552     }
553 
554     private int entriesCount;
555 
556     private void initialize() @nogc
557     {
558         dataSet = Array!(MapBucket)(initializationLength);
559         dataSet.length = initializationLength;
560         dataSet[] = MapBucket.init;
561         countPtr = cast(int*)malloc(int.sizeof);
562         *countPtr = 0;
563     }
564     static auto opCall() @nogc
565     {
566         Map!(K, V) ret;
567         ret.initialize();
568         return ret;
569     }
570 
571 
572 
573     void clear() @nogc 
574     {
575         entriesCount = 0;
576         for(int i = 0; i < dataSet.length; i++)
577         {
578             if(dataSet[i] != MapBucket.init)
579             {
580                 MapBucket* buck = dataSet[i].next;
581                 while(buck != null)
582                 {
583                     MapBucket copy = *buck;
584                     free(buck);
585                     buck = copy.next;
586                 }
587             }
588         }
589     }
590     //Called when array is filled at least increasingThreshold
591     private void recalculateHashes() @nogc
592     {
593         size_t newSize = cast(size_t)(dataSet.capacity * increasingFactor);
594         Array!MapBucket newArray = Array!MapBucket(newSize);
595 
596         for(int i = 0; i < dataSet.length; i++)
597         {
598             if(dataSet[i] != MapBucket.init)
599             {
600                 newArray[hashFunc(dataSet[i].data.key) % newSize] = dataSet[i];
601             }
602         }
603 
604         dataSet = newArray;
605     }
606     
607     bool has(K key) @nogc {return dataSet[getIndex(key)] != MapBucket.init;}
608     pragma(inline) uint getIndex(K key) @nogc {return hashFunc(key) % dataSet.length;}
609 
610 
611     ref auto opIndex(K index) @nogc
612     {
613         if(dataSet.length == 0)
614             initialize();
615         return dataSet[getIndex(index)].data.value;
616     }
617     ref auto opIndexAssign(V value, K key) @nogc
618     {
619         if(dataSet.length == 0)
620             initialize();
621         uint i = getIndex(key);
622         //Unexisting bucket
623         if(dataSet[i] == MapBucket.init)
624         {
625             entriesCount++;
626             dataSet[i] = MapBucket(MapData(key, value), null);
627             if(entriesCount > dataSet.length * increasingThreshold)
628                 recalculateHashes();
629         }
630         else //Existing bucket
631         {
632             MapBucket* curr = &dataSet[i];
633             do
634             {
635                 //Check if the key is the same as the other.
636                 if(curr.data.key == key)
637                 {
638                     curr.data.value = value;
639                     return this;
640                 }
641                 else if(curr.next != null)
642                     curr = curr.next;
643             }
644             while(curr.next != null);
645             curr.next = cast(MapBucket*)malloc(MapBucket.sizeof);
646             *curr.next = MapBucket(MapData(key, value), null);
647 
648         }
649         return this;
650     }
651 
652     auto opBinaryRight(string op, L)(const L lhs) const @nogc
653     if(op == "in")
654     {
655         if(dataSet.length == 0)
656             initialize();
657         uint i = getIndex(key);
658         if(dataSet[i] == MapBucket.init)
659             return null;
660         return &dataSet[i];
661     }
662 
663     ~this() @nogc
664     {
665         if(countPtr !is null)
666         {
667             *countPtr = *countPtr - 1;
668             if(*countPtr == 0)
669             {
670                 //Dispose
671                 clear();
672                 free(countPtr);
673             }
674             countPtr = null;
675         }
676     }
677 
678 }
679 alias AArray = Map;
680 
681 
682 /**
683 *   High efficient(at least memory-wise), tightly packed Input queue that supports any kind of data in
684 *   a single allocated memory pool(no fragmentation).
685 *
686 *   This class could probably be extendeded to be every event handler
687 */
688 class EventQueue
689 {
690     import hip.util.memory;
691     @nogc:
692     
693     struct Event
694     {
695         ubyte type;
696         ubyte evSize;
697         void[0] evData;
698     }
699 
700     ///Linearly allocated variable length Events
701     void* eventQueue;
702     ///BytesOffset should never be greater than capacity
703     uint bytesCapacity;
704     uint bytesOffset;
705     protected uint pollCursor;
706 
707     protected this(uint capacity)
708     {
709         ///Uses capacity*greatest structure size
710         bytesCapacity = cast(uint)capacity;
711         bytesOffset = 0;
712         eventQueue = malloc(bytesCapacity);
713     }
714 
715 
716     void post(T)(ubyte type, T ev)
717     {
718         assert(bytesOffset+T.sizeof+Event.sizeof < bytesCapacity, "InputQueue Out of bounds");
719         if(pollCursor == bytesOffset) //It will restart if everything was polled
720         {
721             pollCursor = 0;
722             bytesOffset = 0;
723         }
724         else if(pollCursor != 0) //It will copy everything from the right to the start
725         {
726             memcpy(eventQueue, eventQueue+pollCursor, bytesOffset-pollCursor);
727             //Compensates the offset into the cursor.
728             bytesOffset-= pollCursor;
729             //Restarts the poll cursor, as everything from the right moved to left
730             pollCursor = 0;
731         }
732         Event temp;
733         temp.type = type;
734         temp.evSize = T.sizeof;
735         memcpy(eventQueue+bytesOffset, &temp, Event.sizeof);
736         memcpy(eventQueue+bytesOffset+Event.sizeof, &ev, T.sizeof);
737         bytesOffset+= T.sizeof+Event.sizeof;
738     }
739 
740     void clear()
741     {
742         //By setting it equal, no one will be able to poll it
743         pollCursor = bytesOffset;
744     }
745 
746     Event* poll()
747     {
748         if(bytesOffset - pollCursor <= 0)
749             return null;
750         Event* ev = cast(Event*)(eventQueue+pollCursor);
751         pollCursor+= ev.evSize+Event.sizeof;
752         return ev;
753     }
754 
755     protected ~this()
756     {
757         free(eventQueue);
758         eventQueue = null;
759         pollCursor = 0;
760         bytesCapacity = 0;
761         bytesOffset = 0;
762     }
763 }
764 
765 class Node(T)
766 {
767     T data;
768     Node!(T)[] children;
769     Node!T parent;
770 
771     this(T data){this.data = data;}
772 
773     pragma(inline) bool isRoot() const @nogc nothrow {return parent is null;}
774     pragma(inline) bool hasChildren() const @nogc nothrow {return children.length != 0;}
775     Node!T get(T data)
776     {
777         foreach(node; this)
778         {
779             if(node.data == data)
780                 return node;
781         }
782         return null;
783     }
784     pragma(inline) Node!T addChild(T data)
785     {
786         Node!T ret = new Node(data);
787         ret.parent = this;
788         children~= ret;
789         return ret;
790     }
791 
792     Node!T getRoot()
793     {
794         Node!T ret = this;
795         while(ret.parent !is null)
796             ret = ret.parent;
797         return ret;
798     }
799     
800     int opApply(scope int delegate(Node!T) cb)
801     {
802         foreach(child; children)
803         {
804             if(cb(child) || child.opApply(cb))
805                 return 1;
806         }
807         return 0;
808     }
809 }
810 
811 struct Signal(A...)
812 {
813     Array!(void function(A)) listeners;
814     void dispatch(A a)
815     {
816         foreach (void function(A) l; listeners)
817             l(a);
818     }
819 }
820 
821 /**
822 * Basically same code from std.array.staticArray but no need to import std.
823 * Use static arrays whenever needing to "fire-and-forget" small arrays
824 */
825 T[N] staticArray(T, size_t N)(auto ref T[N] arr){return arr;}